package com.my.zl.console.common.aop;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSON;
import com.my.zl.common.dto.ReqBaseDTO;
import com.my.zl.common.dto.ReqDTO;

import cn.hutool.core.util.ReflectUtil;


/**  
  * 自动记录日志：统一操作日志记录中心,
  * 
  * 如果加了注解@LogAnnotation（XXXXXX）则会自动记录日志，切点层设置为controller,
  * controller和service都不要使用try_catch来捕获异常,除非这个异常不需要中断程序
  * 异常如果被完全捕获 ,则日志均为doAfterReturning成功状态
  * 
 */ 
@Aspect
@Component
public class LogManagerFilter {
    private static Logger LOGGER = LoggerFactory.getLogger(LogManagerFilter.class);
    private static final ThreadLocal<Long> timeTreadLocal = new ThreadLocal<>();
//  static Map<Long, String> users = Collections.synchronizedMap(new HashMap<Long, String>()); // 创建线程安全的Map
    
    private static final String basePackage = "com.my.zl.console";
    
    //Controller层切点用包路径
    @Pointcut("execution(* "+basePackage+".controller..*(..))")
    public void controllerAspect() {
    }
    @Pointcut("@annotation("+basePackage+".common.aop.NoLogAnnotation)")
    public void controllerNoAspect() {
    }
    @Pointcut("execution(* "+basePackage+".common.controller..*(..))&&@annotation("+basePackage+".common.aop.LogAnnotation)")    
    public void operationAspect() {
    }

    //请求日志记录
    @Before("controllerAspect()")
    public void before(JoinPoint joinPoint) {
        timeTreadLocal.set(System.currentTimeMillis());
        
        addExtraParam(joinPoint);//临时方法,这里处理下接收对象
        
        // 接收请求，记录完整请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //获取请求的request
        HttpServletRequest request = attributes.getRequest();
        
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //获取被拦截的方法
        Method method = methodSignature.getMethod();
        //获取被拦截的方法名
        String methodName = method.getName();
        LOGGER.info("AOP begin");
        LOGGER.info("调用controller方法  :{}", method.getDeclaringClass() + "." + methodName + "()");
        
        //获取所有请求的参数
        String keyValue = getReqData(joinPoint);
//        LOGGER.info("请求url = {}", request.getRequestURL().toString());
        LOGGER.info("请求uri = {},请求Method = {},{}",request.getRequestURI(),request.getMethod(),request.getContentType());
        LOGGER.info("请求参数 key：value = {}", keyValue);
    }
    //后置通知
    @AfterReturning(pointcut = "controllerAspect() && !controllerNoAspect()",returning = "result")    
    public void doAfterReturning(JoinPoint joinPoint,Object result)  {
        long startTime = timeTreadLocal.get();
        double callTime = (System.currentTimeMillis() - startTime) / 1000.0;
        LOGGER.info("返回数据={}", JSON.toJSONString(result));
        LOGGER.info("调用controller花费时间time = {}s,正常返回", callTime);
        LOGGER.info("AOP end");
        timeTreadLocal.remove();
    }
    @AfterReturning(pointcut = "controllerAspect() && controllerNoAspect()",returning = "result")    
    public void doAfterReturningSimple(JoinPoint joinPoint,Object result)  {
        long startTime = timeTreadLocal.get();
        double callTime = (System.currentTimeMillis() - startTime) / 1000.0;
        LOGGER.info("返回数据={}", "不输出，输出内容省略...");
        LOGGER.info("调用controller花费时间time = {}s,正常返回", callTime);
        LOGGER.info("AOP end");
        timeTreadLocal.remove();
    }  
    //后置通知 
    @AfterThrowing(pointcut = "controllerAspect()", throwing = "e")    
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        long startTime = timeTreadLocal.get();
        double callTime = (System.currentTimeMillis() - startTime) / 1000.0;
        LOGGER.info("调用controller花费时间time = {}s,异常返回", callTime);
        LOGGER.info("AOP end");
        timeTreadLocal.remove();
    }
    
    /**所有请求参数*/
    private String getReqData(JoinPoint joinPoint){
        Object[] obj = joinPoint.getArgs();
        try{
            return JSON.toJSONString(obj);
        }catch(Exception e){
        }
        return "";
    }
    
    
    /**临时方法后面注释*/
    private void addExtraParam(JoinPoint joinPoint){
        Object[] obj = joinPoint.getArgs();
        for (Object obj1 : obj) {
            if (obj1 instanceof ReqDTO || obj1 instanceof ReqBaseDTO) {
                ReflectUtil.setFieldValue(obj1, "reqUserId", "9999999999");
                ReflectUtil.setFieldValue(obj1, "reqUserName", "zltest");
                break;
            }
        }
    }
}    